fix(fs): expose iterator helpers on walk/walkSync and expandGlob/expandGlobSync#7136
fix(fs): expose iterator helpers on walk/walkSync and expandGlob/expandGlobSync#7136fibibot wants to merge 2 commits into
walk/walkSync and expandGlob/expandGlobSync#7136Conversation
…`/`expandGlobSync`
The four generator functions in `@std/fs` were annotated with the
`IterableIterator<WalkEntry>` / `AsyncIterableIterator<WalkEntry>`
return types, which widen the actual `Generator` / `AsyncGenerator`
runtime type and hide the ES2025 iterator helpers (`.map`, `.filter`,
`.take`, etc.) at the type level.
Switch the annotations to `Generator<WalkEntry>` /
`AsyncGenerator<WalkEntry>` so callers can chain helpers without
widening, e.g. `walkSync(".").map((v) => v.name)`.
Closes #7099
|
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #7136 +/- ##
=======================================
Coverage 94.61% 94.61%
=======================================
Files 634 634
Lines 51822 51826 +4
Branches 9336 9348 +12
=======================================
+ Hits 49029 49035 +6
+ Misses 2218 2216 -2
Partials 575 575 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
The previous commit added `Generator<WalkEntry>` annotations and tests that exercise `.map(...).toArray()`. Both regressed on Deno v1.x (TypeScript 5.5, which predates iterator helpers in `lib.es2025.iterator.d.ts`): - `expandGlob` / `expandGlobSync` ended with `yield* currentMatches`, where `currentMatches` is `WalkEntry[]`. Array's iterator declares `TNext` as `undefined`, but the outer `Generator<WalkEntry>`'s `TNext` defaults to `unknown`, so TS 5.5 reports TS2766. Newer TS widens the array iterator to `unknown` and the call type-checks. Replace the delegation with an explicit `for ... yield` loop so it compiles on both. - The new tests called `.map(...).toArray()` on the returned iterator, which TS 5.5 rejects (no iterator helpers in lib). Switch to a type-level assertion: `const iter: Generator<WalkEntry> = walkSync(...)`. This still enforces the regression — `Generator<T>` requires the `.return()`/`.throw()` methods that `IterableIterator<T>` leaves optional, so the assignment fails if the return type widens back — but compiles on every TS version where `Generator<T>` is defined.
bartlomieju
left a comment
There was a problem hiding this comment.
The core type fix (IterableIterator → Generator) is correct and the regression tests are solid. Two items before merge — see inline comments for the unnecessary yield* rewrite, and please strip the Closes bartlomieju/orchid-inbox#62 line from the PR description since it points at a private repo and won't resolve for other reviewers.
Optional: the async-generator tests only call .return(undefined). That's enough to prove the type shape, but iterating with for await and asserting at least one yield would also guard against a runtime regression.
Minor caveat for release notes: narrowing the return type to Generator<T> / AsyncGenerator<T> is strictly more specific than IterableIterator<T>. Existing callers should be fine (Generator extends IterableIterator), but anyone who mocked the return value with a hand-rolled iterable literal would now fail type-checks.
| yield* currentMatches; | ||
| for (const match of currentMatches) { | ||
| yield match; | ||
| } |
There was a problem hiding this comment.
This rewrite isn't needed. currentMatches is unambiguously WalkEntry[] here (assigned from [...map.values()].sort(...) and .filter(...)), so yield* currentMatches and the explicit for...of are behaviorally identical and both typecheck under the new AsyncGenerator<WalkEntry> annotation. Please revert to yield* currentMatches; to keep this PR a pure type-only fix.
| yield* currentMatches; | ||
| for (const match of currentMatches) { | ||
| yield match; | ||
| } |
There was a problem hiding this comment.
Same as the async version above — currentMatches is a plain array, so yield* currentMatches; works fine under Generator<WalkEntry>. Please revert this hunk.
Summary
walk,walkSync,expandGlob, andexpandGlobSyncare generator functions, but their return-type annotations widened the type to(Async)IterableIterator<WalkEntry>, hiding the ES2025 iterator helpers (.map,.filter,.take, …) at the type level. The runtime objects do have those helpers (on the sync side), so the original reprowas a type-only bug.
This PR changes the annotations to
Generator<WalkEntry>/AsyncGenerator<WalkEntry>for all four functions so callers can chainhelpers without widening, and the async pair is forward-compatible for
when async iterator helpers ship in the standard library.
Note:
walkandwalkSyncneed an explicit return type (TS7023 — theyreference themselves recursively), so the annotations are tightened
rather than dropped entirely.
Test plan
Regression tests are co-located in
fs/walk_test.tsandfs/expand_glob_test.ts. The sync tests exercise.map(...).toArray()at both type-check and runtime; the async tests assert that
AsyncGeneratorshape is preserved (no async iterator helpers exist atruntime yet, so they just exercise
.next()/.return()).deno fmt --checkdeno lintdeno run -A _tools/check_docs.tswalkSync()/expandGlobSync() returns a generator that exposes iterator helpers,walk()/expandGlob() returns an async generatorwalkSyncreturns a type without iterator helpers #7099 type-checksCloses #7099
Closes bartlomieju/orchid-inbox#62